package com.codingfly.loadbalancer;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.reactive.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.reactive.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.reactive.Request;
import org.springframework.cloud.client.loadbalancer.reactive.Response;
import org.springframework.cloud.loadbalancer.core.*;
import org.springframework.http.HttpHeaders;
import reactor.core.publisher.Mono;

import java.util.*;

public class GrayLoadBalancer implements ReactorServiceInstanceLoadBalancer {
    private static final Log log = LogFactory.getLog(GrayLoadBalancer.class);
    private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
    private String serviceId;
    private String grayDomain; // 灰度域名

    public String getGrayDomain() {
        return grayDomain;
    }

    public void setGrayDomain(String grayDomain) {
        this.grayDomain = grayDomain;
    }

    public GrayLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
        this.serviceId = serviceId;
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
    }

    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        HttpHeaders headers = (HttpHeaders) request.getContext();
        if (this.serviceInstanceListSupplierProvider != null) {
            ServiceInstanceListSupplier supplier = this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
            return supplier.get().next().map(list->getInstanceResponse(list, headers));
        }
        return null;
    }

    private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances, HttpHeaders headers) {
        if (instances.isEmpty()) {
            return getServiceInstanceEmptyResponse();
        } else {
            return getServiceInstanceResponseByVersion(instances, headers);
        }
    }

    /**
     * 根据版本，域名进行转发分发
     * @param instances
     * @param headers
     * @return
     */
    private Response<ServiceInstance> getServiceInstanceResponseByVersion(List<ServiceInstance> instances, HttpHeaders headers) {
        String version = headers.getFirst("version");
        boolean currentReqIsGrayReq =  "gray".equals(version);
        String currentDomain = headers.getFirst("host");
        boolean currentDomainIsGrayDomain = false;
        if (!StringUtils.isEmpty(currentDomain)) {
            currentDomainIsGrayDomain = currentDomain.equals(grayDomain);
        }
        for (ServiceInstance instance : instances) {
            // 灰度的请求
            if (currentDomainIsGrayDomain || currentReqIsGrayReq) {
                if (isGrayInstance(instance)) {
                    return new DefaultResponse(instance);
                }
            // 非灰度的请求
            } else {
                if (!isGrayInstance(instance)) {
                    return new DefaultResponse(instance);
                }
            }
        }
        // 都找不到，直接返回空服务实例
        return getServiceInstanceEmptyResponse();
    }

    public boolean isGrayInstance(ServiceInstance instance) {
        Map<String, String> metadata = instance.getMetadata();
        return "gray".equals(metadata.get("version"));
    }
    private Response<ServiceInstance> getServiceInstanceEmptyResponse() {
        log.warn("No servers available for service: " + this.serviceId);
        return new EmptyResponse();
    }

}